package cn.maihe.elg.operation.centers.supports;

import cn.maihe.elg.operation.centers.follow.handler.SignReqDTO;
import cn.maihe.elg.operation.centers.supports.annotation.CenterTradeService;
import cn.maihe.elg.operation.common.auth.Authorization;
import cn.maihe.elg.operation.common.auth.Encryption;
import cn.maihe.elg.operation.exception.CenterException;
import cn.maihe.elg.operation.model.dto.BaseReq;
import cn.maihe.elg.operation.model.dto.BaseResp;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.DelFlagEnum;
import cn.maihe.elg.operation.model.enums.InstitutionCode;
import cn.maihe.elg.operation.model.enums.InstitutionOrgCodeEnum;
import cn.maihe.elg.operation.repository.entity.ExchangeCenterDO;
import cn.maihe.elg.operation.repository.entity.GuaranteeInstitutionDO;
import cn.maihe.elg.operation.repository.entity.OrganizationDO;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.framework.starter.mybatis.dao.MybatisDaoImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Description
 * @Author WJH
 * @Date 2021/04/15
 */
@Slf4j
public abstract class BaseCenterService<REQ extends BaseReq, RESP extends BaseResp> {

    protected final ObjectMapper objectMapper = new ObjectMapper();
    protected Authorization authorization;
    protected Encryption encryption;
    protected CenterTradeService tradeService;
    protected Class<REQ> reqClass;
    protected Class<RESP> respClass;
    protected final CenterNoEnum centerNoEnum;

    public BaseCenterService() {
        this.tradeService = getClass().getDeclaredAnnotation(CenterTradeService.class);
        Assert.notNull(tradeService, this.getClass().getName().concat("未添加注解@CenterTradeService"));
        this.centerNoEnum = this.tradeService.centerNo();
        this.reqClass = (Class<REQ>) this.tradeService.reqClass();
        this.respClass = (Class<RESP>) this.tradeService.respClass();
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    protected void init(Authorization authorization, Encryption encryption) {
        this.authorization = authorization;
        this.encryption = encryption;
    }

    protected CenterNoEnum getCenterNoEnum() {
        return this.centerNoEnum;
    }

    /**
     * 交易中心服务入口 TODO 待后续提取共用
     *
     * @param reqParams
     * @param attrMap   附加属性值
     * @return
     */
    public BaseResp doService(String orgCode, Map<String, Object> reqParams, Map<String, Object> attrMap) {

//        ElgLogger.info(log, log -> log.info("[{}:{}][{}] => 原始请求报文:{}", centerNoEnum.getCenterNo(), orgCode, tradeService.tradeType()));
//
//        byte[] encryptAy = this.map2Byte(reqParams);
//        //解密
//        byte[] decryptAy = this.decrypt(encryptAy);
//
//        //验签
//        this.verifySignature(decryptAy);

        //参数校验

        //插入库表

        //更新库表

        //组装响应报文

        //私钥加密

        //签名


        return null;
    }

    /**
     * 机构缓存
     */
    protected static Cache<String, OrganizationDO> orgCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
    /**
     * 交易中心缓存：外部入口统一是db中的中心编码，对接一个内置一个不可修改
     */
    protected static Cache<String, ExchangeCenterDO> centerCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
    /**
     * 出函机构缓存
     */
    protected static Cache<String, Map<String, GuaranteeInstitutionDO>> institutionCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();


    protected Long getOrgId(String orgCode) {
        //TODO 后期待综合考虑
        OrganizationDO org = null;
        try {
            org = orgCache.get(orgCode, () -> {
                return MybatisDaoImpl.run().selectOne(OrganizationDO.builder().orgCode(orgCode).delFlag(DelFlagEnum.NORMAL.getValue()).build());
            });
            if (org == null) {
                ElgLogger.error(log, l -> l.error("查无此可用的机构编码[{}]", orgCode));
                throw CenterException.error("查无此可用的机构编码", null);
            }
        } catch (Exception e) {
            ElgLogger.error(log, l -> l.error("查无此可用的机构编码[{}]", orgCode, e));
            throw CenterException.exception("查无此可用的机构编码", e);
        }

        orgCache.put(orgCode, org);
        return org.getId();

    }

    /**
     * 内置交易中心编码与库表的映射
     */
    protected Long getCurCenterId(CenterNoEnum centerNoEnum) {

        ExchangeCenterDO centerDO = null;
        try {
            centerDO = centerCache.get(centerNoEnum.getCenterNo(), () -> {
                return MybatisDaoImpl.run().selectOne(ExchangeCenterDO.builder().centerCode(centerNoEnum.getCenterNo()).build());
            });
            if (centerDO == null) {
                ElgLogger.error(log, l -> l.error("查无此可用的交易中心编码[{}]", centerNoEnum.getCenterNo()));
                throw CenterException.error("查无此可用的交易中心编码", null);
            }
        } catch (Exception e) {
            ElgLogger.error(log, l -> l.error("查无此可用的交易中心编码[{}]", centerNoEnum.getCenterNo(), e));
            throw CenterException.error("查无此可用的交易中心编码", e);
        }

        centerCache.put(centerNoEnum.getCenterNo(), centerDO);
        return centerDO.getId();

    }

    /**
     * 出函机构编码也是内置：关联库表id
     */
    protected GuaranteeInstitutionDO getInstitutionIdByCode(InstitutionCode institutionCode) {
        GuaranteeInstitutionDO institutionDO = MybatisDaoImpl.run().selectOne(GuaranteeInstitutionDO.builder().institutionCode(institutionCode.getCode()).build());
        if (institutionDO == null) {
            throw new RuntimeException("查无此出函机构编码[" + institutionCode.getCode() + "]记录");
        }
        return institutionDO;
    }

    /**
     * 出函机构编码也是内置：关联库表id
     */
    protected GuaranteeInstitutionDO getInstitutionIdByCode(InstitutionOrgCodeEnum institutionOrgCodeEnum) {
        GuaranteeInstitutionDO institutionDO = MybatisDaoImpl.run().selectOne(GuaranteeInstitutionDO.builder().institutionCode(institutionOrgCodeEnum.getOrgCode()).build());
        if (institutionDO == null) {
            throw new RuntimeException("查无此出函机构编码[" + institutionOrgCodeEnum.getOrgCode() + "]记录");
        }
        return institutionDO;
    }

    /**
     * 出函机构编码也是内置：关联库表id
     */
    protected GuaranteeInstitutionDO getGuaranteeInstitutionByInstitutionId(Long institutionId) {
        GuaranteeInstitutionDO institutionDO = MybatisDaoImpl.run().selectOne(GuaranteeInstitutionDO.builder().id(institutionId).build());
        if (institutionDO == null) {
            throw new RuntimeException("查无此出函机构编码[" + institutionId + "]记录");
        }
        return institutionDO;
    }

    protected String toJsonString(Object body) {
        try {
            if (body instanceof byte[]) {
                Map<String, Object> tmp = objectMapper.readValue((byte[]) body, new TypeReference<Map<String, Object>>() {
                });
                return objectMapper.writeValueAsString(tmp);
            }
            return objectMapper.writeValueAsString(body);
        } catch (Exception e) {
            throw CenterException.exception("转换json字符串异常" ,e);
        }
    }

    protected long yuan2fenLong(String yuan) {
        try {
            return new BigDecimal(yuan).multiply(BigDecimal.valueOf(100)).longValue();
        } catch (Exception e) {
            throw CenterException.error("元转分失败", e);
        }
    }

    protected String fen2yuanString(Long fen) {
        try {
            return BigDecimal.valueOf(fen).divide(BigDecimal.valueOf(100L), 2, RoundingMode.HALF_UP).toString();
        } catch (Exception e) {
            throw CenterException.error("分转元失败", e);
        }
    }


    /**
     * 交易中心服务入口 TODO 待后续提取共用
     * @param centerNoEnum
     * @param orgCode
     * @param reqParams
     * @param attrMap
     * @return
     */
    public BaseResp doService(CenterNoEnum centerNoEnum, String orgCode, Map<String, Object> reqParams, Map<String, Object> attrMap){
        return null;
    }

    public RESP doService(String orgCode, SignReqDTO reqParams, String params, Map<String, Object> attrMap){
        return null;
    }
}
